import {
    ApiMsgEnum, EntityTypeEnum, IClientInput, IMsgClientSync, IState, InputTypeEnum, GSyncDelay, StateTypeEnum, toFixed, deepClone,
    G, IActorMove, ITimePass, IWeapon
} from "../Common"
import { Connection } from "../Core/Connection"
import { Player } from "./Player"
import { PlayerManager } from "./PlayerManager"
import { RoomManager } from "./RoomManager"
import QuadTree from "../Common/QuadTree"
import { Simulator } from "../Common/RVO/Simulator"
import { BehaviorManager } from "../Common/BehaviorTree/BehaviorManager"
import { GameLogic } from "../Common/Runtime/GameLogic"

const weaponTemp1: IWeapon = {
    id: 0,
    owner: 0,
    entityType: EntityTypeEnum.weapon1,
    position: { x: -20, y: -25 },
    direction: { x: 1, y: 0 },
    shootCD: 0,
    searchCD: 0,
    targetId: -1,
    state: StateTypeEnum.none,
    star: 1,
}

const weaponTemp2: IWeapon = {
    id: 0,
    owner: 0,
    entityType: EntityTypeEnum.weapon2,
    position: { x: 20, y: -25 },
    direction: { x: 1, y: 0 },
    shootCD: 0,
    searchCD: 0,
    targetId: -1,
    state: StateTypeEnum.none,
    star: 1,
}
const actors = [
    EntityTypeEnum.actor_red,
    EntityTypeEnum.actor_green,
    EntityTypeEnum.actor_blue,
    EntityTypeEnum.actor_yellow,
]
const initPos = [
    { x: 50, y: 50 },
    { x: 50, y: -50 },
    { x: -50, y: -50 },
    { x: -50, y: 50 },
]
export class Room {

    state: IState
    isGaming: boolean
    rid: number
    players: Set<Player> = new Set()
    times: NodeJS.Timeout[] = []
    behaviorTreeMap: Map<number, BehaviorManager> = new Map()
    simulator: Simulator
    /** 申请中途加入 已经开始的游戏 的玩家 */
    private applyPlayers: Player[] = []
    private quadTree: QuadTree | null = null
    private pendingInput: IClientInput[] = []
    private lastPlayerFrameId: Map<number, number> = new Map()
    private lastTime: number
    private nextPlayerIndex = 0
    constructor(rid: number) {
        this.rid = rid
        this.lastTime = 0
        this.isGaming = false
        this.simulator = new Simulator()
        this.state = {
            actors: [],
            bullets: [],
            enemyBullets: [],
            enemyBuffs: [],
            enemySkills: [],
            nextId: 1,
            enemies: [],
            weapons: [],
            props: [],
            exps: [],
            redXs: [],
            dt: 0,
            seed: Math.floor(Math.random() * 99999),
            isPause: 0,
            gameSpeed: 1,
            playerExp: 0,
            playerLevel: 1,
            state: StateTypeEnum.idle,
            skill: [],

            levelState: {
                normalLevelTime: 0,
                totalTime: 0,
                totalLevel: 0,
                levelTime: 0,
                partEnemyTime: {},
                bossArr: [],
                chapter: 1,
                level: 1,
                levelType: 0
            }
        }
    }

    /** 加入房间 */
    join(id: number) {
        const player = PlayerManager.Ins.idMapPlayers.get(id)
        if (player) {
            player.rid = this.rid
            player.ready = 0
            this.players.add(player)
            const owner = [...this.players][0]
            if (owner) {
                owner.ready = 1
            }
        }
    }

    /** 离开房间 */
    leave(id: number) {
        const player = PlayerManager.Ins.idMapPlayers.get(id)
        if (player) {
            player.rid = 0
            this.players.delete(player)
            player.connection.unlistening(ApiMsgEnum.MsgClientSync, this.getClientMsg, this)
            const owner = [...this.players][0]
            if (owner) {
                owner.ready = 1
            }
        }
        if (this.players.size <= 0) {
            RoomManager.Ins.closeRoom(this)
        }
    }

    /** 申请加入正在进行的游戏 */
    applyJoneGaming(player: Player) {
        this.applyPlayers.push(player)
    }

    /** 加入正在进行的游戏 */
    jionGaming({ id }: Player) {
        const players = [...this.players]
        const player = players.find(v => v.id === id)
        if (player) {
            player.connection.listening(ApiMsgEnum.MsgClientSync, this.getClientMsg, this)
            this.state.actors.push({
                id: player.id,
                nickName: player.nickName,
                type: actors[this.nextPlayerIndex],
                position: deepClone(initPos[this.nextPlayerIndex++]),
                direction: { x: 1, y: 0 },
                hp: 1,
                state: StateTypeEnum.none,
                skillPoint: 0,
                beHitCD: 0,
                skill: [],
                attr: {
                    hpMax: 0,
                    defense: 0,
                    damage: 0,
                    growth: 0,
                    regeneration: 0,
                    knckback: 0,
                    range: 0,
                    fireRate: 0,
                    lootRange: 0,
                    critRate: 0,
                    critChance: 0,
                    moveSpeed: 0,
                    hpMaxRate: 0,
                    defenseRate: 0,
                    damageRate: 0
                },
                regenerationCD: 0
            })

            const weapon1 = deepClone(weaponTemp1)
            weapon1.id = this.state.nextId++
            weapon1.owner = player.id
            this.state.weapons.push(weapon1)

            const weapon2 = deepClone(weaponTemp2)
            weapon2.id = this.state.nextId++
            weapon2.owner = player.id
            this.state.weapons.push(weapon2)
        }
    }

    /** 向房间内所有玩家发广播，同步房间内信息 */
    syncRoom() {
        for (const { rid, connection } of this.players) {
            if (rid !== 0) {
                connection.sendMsg(ApiMsgEnum.MsgRoomSync, {
                    room: RoomManager.Ins.getRoomView(this)
                })
            }
        }
    }

    /** 房间开始游戏 */
    gameStart() {
        this.isGaming = true
        this.initGame()
        this.players.forEach(player => {
            player.isGaming = true
            player.connection.sendMsg(ApiMsgEnum.MsgGameStart, { state: this.state })
            player.connection.listening(ApiMsgEnum.MsgClientSync, this.getClientMsg, this)
        })

        const t1 = setInterval(() => {
            this.sendServerMsg()
        }, GSyncDelay)

        const t2 = setInterval(() => {
            this.timePass()
        }, 16)

        this.times = [t1, t2]

        RoomManager.Ins.syncHall()
    }

    /** 初始化游戏内容 */
    initGame() {

        GameLogic.Ins.initAgent(this.simulator)

        this.lastTime = 0
        this.state = {
           
            actors: [],
            bullets: [],
            enemyBullets: [],
            enemyBuffs: [],
            enemySkills: [],
            nextId: 1,
            enemies: [],
            weapons: [],
            props: [],
            exps: [],
            redXs: [],
            dt: 0,
            seed: Math.floor(Math.random() * 99999),
            isPause: 0,
            gameSpeed: 1,
            playerExp: 0,
            playerLevel: 1,
            state: StateTypeEnum.idle,
            skill: [],

            levelState: {
                normalLevelTime: 0,
                totalTime: 0,
                totalLevel: 0,
                levelTime: 0,
                partEnemyTime: {},
                bossArr: [],
                chapter: 1,
                level: 1,
                levelType: 0
            }
        }

        // 构造四叉树
        this.quadTree = new QuadTree({
            x: -G.StageSize.w / 2,
            y: -G.StageSize.h / 2,
            w: G.StageSize.w,
            h: G.StageSize.h,
        }, 1)

        const players = [...this.players]
        for (const i in players) {
            const player = players[i]
            this.state.actors.push({
                id: player.id,
                nickName: player.nickName,
                type: actors[this.nextPlayerIndex],
                position: deepClone(initPos[this.nextPlayerIndex++]),
                direction: { x: 1, y: 0 },
                hp: 1,
                state: StateTypeEnum.none,
                skillPoint: 0,
                beHitCD: 0,
                skill: [],
                attr: {
                    hpMax: 0,
                    defense: 0,
                    damage: 0,
                    growth: 0,
                    regeneration: 0,
                    knckback: 0,
                    range: 0,
                    fireRate: 0,
                    lootRange: 0,
                    critRate: 0,
                    critChance: 0,
                    moveSpeed: 0,
                    hpMaxRate: 0,
                    defenseRate: 0,
                    damageRate: 0
                },
                regenerationCD: 0
            })

            const weapon1 = deepClone(weaponTemp1)
            weapon1.id = this.state.nextId++
            weapon1.owner = player.id
            this.state.weapons.push(weapon1)

            const weapon2 = deepClone(weaponTemp2)
            weapon2.id = this.state.nextId++
            weapon2.owner = player.id
            this.state.weapons.push(weapon2)
        }

        GameLogic.Ins.setLevelData(this.state)
    }

    /** 时间流逝 */
    timePass() {
        const now = process.uptime()
        const dt = toFixed(now - (this.lastTime || now)) * this.state.gameSpeed
        this.state.levelState.totalTime = toFixed(this.state.levelState.totalTime + dt)
        this.lastTime = now
        const input = {
            type: InputTypeEnum.timePass,
            seed: this.state.seed++,
            dt,
            totalTime: this.state.levelState.totalTime,
        }
        this.pendingInput.push(input)
        this.applyInput(input)
    }

    applyInput(input: IClientInput) {
        if (input.type == InputTypeEnum.actorMove) {
            this.state = GameLogic.Ins.actorMove(input as IActorMove, this.state)
        } else if (input.type == InputTypeEnum.timePass) {
            const { state } = GameLogic.Ins.tick(input as ITimePass, this.state, this.quadTree as QuadTree, this.behaviorTreeMap, this.simulator)
            this.state = state
        }
    }

    /** 处理客户端输入 */
    getClientMsg(connection: Connection, { frameId, input }: IMsgClientSync) {
        input.dt *= this.state.gameSpeed
        this.lastPlayerFrameId.set(connection.playerId, frameId)
        this.pendingInput.push(input)
        this.applyInput(input)
    }

    sendServerMsg() {

        // 有人中途加入
        if (this.applyPlayers.length) {
            for (const player of this.applyPlayers) {
                player.readyJoiningMidGame = true
                this.jionGaming(player)
            }
            this.players.forEach((player) => {
                const { connection, id, isOnline, readyJoiningMidGame, isGaming } = player
                if (isOnline && isGaming && !readyJoiningMidGame) {
                    connection.sendMsg(ApiMsgEnum.MsgServerSync, {
                        state: this.state,
                        agentId: this.simulator.getAgentId(),
                        lastFrameId: this.lastPlayerFrameId.get(id) ?? 0,
                        inputs: this.pendingInput,
                    })
                } else if (isOnline && !isGaming && readyJoiningMidGame) {
                    player.isGaming = true
                    player.readyJoiningMidGame = false
                    connection.sendMsg(ApiMsgEnum.MsgStateSync, {
                        state: this.state,
                        agentId: this.simulator.getAgentId()
                    })
                }
            })

            this.applyPlayers = []
        } else {
            this.players.forEach((player) => {
                const { connection, id, isOnline, readyJoiningMidGame, isGaming } = player
                if (isOnline) {
                    if (readyJoiningMidGame) {
                        player.readyJoiningMidGame = false
                        connection.sendMsg(ApiMsgEnum.MsgStateSync, {
                            state: this.state,
                            agentId: this.simulator.getAgentId()
                        })
                    } else if (isGaming) {
                        connection.sendMsg(ApiMsgEnum.MsgServerSync, {
                            lastFrameId: this.lastPlayerFrameId.get(id) ?? 0,
                            inputs: this.pendingInput,
                        })
                    }
                }
            })
        }

        this.pendingInput = []

    }

    /** 房间内玩家掉线 */
    playerOffline(player: Player): boolean {
        if (this.isGaming) {
            player.isOnline = false
            player.connection.unlistening(ApiMsgEnum.MsgClientSync, this.getClientMsg, this)
            // 还有其他玩家在线
            for (const player of this.players) {
                if (player.isOnline) {
                    return true
                }
            }

            // 所有玩家都掉线了
            this.removeAllPlayer()

            return true
        } else {
            this.leave(player.id)
            this.syncRoom()
            RoomManager.Ins.syncHall()
            return false
        }
    }

    /** 移除所有玩家 */
    removeAllPlayer() {
        const players = [...this.players]
        for (let i = players.length - 1; i >= 0; i--) {
            console.log(`玩家${players[i].id}断开了链接`)
            PlayerManager.Ins.removePlayer(players[i].id)
        }
        RoomManager.Ins.closeRoom(this)
        RoomManager.Ins.syncHall()
    }
}